//
// Created by Administrator on 2020/10/14.
//

#include <getopt.h>
#include <sys/stat.h>
#include "../include/ranApi.h"
#include "../l1/include/l1Api.h"
#include "../l2/include/l2Api.h"
#include "../l3/include/l3Api.h"
#include "../../ccore/misc/include/misc.h"
#include "../../../lib/3rdParty/toml/toml.h"

S_RanContext* gpRanContext = NULL;

int32_t RAN_LoadCellConfig(toml_table_t* pCellTable, S_RanCellContext *context)
{
    int32_t rc;

    /* cell_id */
    TOML_GET_INT_VALUE(pCellTable, "cell_id", context->cellId);
    CSP_LOG_INF(RAN_MD_COMM, "cell_id : %u\n", context->cellId);

    /* active */
    TOML_GET_BOOL_VALUE(pCellTable, "active", context->inUse);
    CSP_LOG_INF(RAN_MD_COMM, "active : %u\n", context->inUse);

    /* dl_subcarrier_spacing */
    TOML_GET_INT_VALUE(pCellTable, "dl_subcarrier_spacing", context->dlSubcarrierSpacing);
    CSP_LOG_INF(RAN_MD_COMM, "dlSubcarrierSpacing : %u\n", context->dlSubcarrierSpacing);

    return O5G_RC_OK;
}

int32_t RAN_LoadNodeBConfig(toml_table_t* pNodeBTable, S_RanNodeBContext *context)
{
    int32_t rc;
    uint32_t cellLoop;
    uint32_t cellNum;
    char *ipStr;

    /* nodeb_id */
    TOML_GET_INT_VALUE(pNodeBTable, "nodeb_id", context->nbId);
    CSP_LOG_INF(RAN_MD_COMM, "nodeb_id : %u\n", context->nbId);

    /* active */
    TOML_GET_BOOL_VALUE(pNodeBTable, "active", context->inUse);
    CSP_LOG_INF(RAN_MD_COMM, "active : %u\n", context->inUse);

    /* PLMN */
    toml_table_t* pPlmnTable;
    pPlmnTable = toml_table_in(pNodeBTable, "plmn");
    IF_RETURN(==, NULL, pPlmnTable, O5G_RC_ER, "RAN_LoadNodeBConfig() toml_table_in(plmn) error!\n");

    TOML_GET_INT_VALUE(pPlmnTable, "mcc", context->plmn.mcc);
    TOML_GET_INT_VALUE(pPlmnTable, "mnc", context->plmn.mnc);
    TOML_GET_INT_VALUE(pPlmnTable, "mnc_length", context->plmn.mncLen);

    CSP_LOG_INF(RAN_MD_COMM, "mcc : %u, mnc : %u, mncLen : %u\n",
        context->plmn.mcc, context->plmn.mnc, context->plmn.mncLen);

    /* AMF IP */
    toml_table_t* pAmfIpTable;
    pAmfIpTable = toml_table_in(pNodeBTable, "amf_ip_address");
    IF_RETURN(==, NULL, pAmfIpTable, O5G_RC_ER, "RAN_LoadNodeBConfig() toml_table_in(amf_ip_address) error!\n");

    TOML_GET_STR_VALUE(pAmfIpTable, "ipv4", ipStr);
    strncpy(context->amfIp.addr4, ipStr, INET_ADDRSTRLEN);
    free(ipStr);
    context->amfIp.ipv4 = TRUE;

    CSP_LOG_INF(RAN_MD_COMM, "AMF IP : %s\n", (char *)context->amfIp.addr4);

    /* UPF IP */
    toml_table_t* pUpfIpTable;
    pUpfIpTable = toml_table_in(pNodeBTable, "upf_ip_address");
    IF_RETURN(==, NULL, pUpfIpTable, O5G_RC_ER, "RAN_LoadNodeBConfig() toml_table_in(amf_ip_address) error!\n");

    TOML_GET_STR_VALUE(pAmfIpTable, "ipv4", ipStr);
    strncpy(context->upfIp.addr4, ipStr, INET_ADDRSTRLEN);
    free(ipStr);
    context->upfIp.ipv4 = TRUE;

    CSP_LOG_INF(RAN_MD_COMM, "UPF IP : %s\n", (char *)context->upfIp.addr4);

    /* CELLS */
    toml_array_t* pCellTableArray;
    pCellTableArray = toml_array_in(pNodeBTable, "cell_list");
    IF_RETURN(==, NULL, pCellTableArray, O5G_RC_ER, "RAN_LoadNodeBConfig() toml_parse_file() error!\n");

    cellNum = toml_array_nelem(pCellTableArray);
    IF_RETURN(<, RAN_CELL_NUM_MAX, cellNum, O5G_RC_ER, "RAN_LoadNodeBConfig() cellNum[%d] error!\n", cellNum);

    context->cellNum = cellNum;
    for (cellLoop = 0; cellLoop < cellNum; cellLoop++)
    {
        toml_table_t* pCellTable;
        pCellTable = toml_table_at(pCellTableArray, cellLoop);
        IF_RETURN(==, NULL, pCellTable, O5G_RC_ER, "RAN_LoadNodeBConfig() toml_table_at() error!\n");

        rc = RAN_LoadCellConfig(pCellTable, &context->cellContext[cellLoop]);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_LoadNodeBConfig() RAN_LoadCellConfig() error!\n");
    }

    return O5G_RC_OK;
}

int32_t RAN_LoadConfig()
{
    int32_t rc;
    uint32_t nodeLoop;
    uint32_t nodeBNum;
    struct stat buf;
    char tomlErrBuffer[128];

    if (lstat(gpRanContext->configFilePath, &buf) < 0)
    {
        CSP_LOG_ERR(RAN_MD_COMM, "RAN_LoadConfig() configFile[%s] error!\n", (char *)gpRanContext->configFilePath);
        return O5G_RC_ER;
    }
    if (!S_ISREG(buf.st_mode))
    {
        CSP_LOG_ERR(RAN_MD_COMM, "RAN_LoadConfig() configFileMode[%s] error!\n", (char *)gpRanContext->configFilePath);
        return O5G_RC_ER;
    }

    FILE* fp = fopen(gpRanContext->configFilePath, "rb");
    IF_RETURN(==, NULL, fp, O5G_RC_ER, "RAN_LoadConfig() fopen() error!\n");

    toml_table_t* pTomlTableRoot;
    pTomlTableRoot = toml_parse_file(fp, tomlErrBuffer, sizeof(tomlErrBuffer));
    IF_RETURN(==, NULL, pTomlTableRoot, O5G_RC_ER, "RAN_LoadConfig() toml_parse_file() error!\n");

    toml_array_t* pNodeBTableArray;
    pNodeBTableArray = toml_array_in(pTomlTableRoot, "nodeb_list");
    IF_RETURN(==, NULL, pNodeBTableArray, O5G_RC_ER, "RAN_LoadConfig() toml_parse_file() error!\n");

    nodeBNum = toml_array_nelem(pNodeBTableArray);
    IF_RETURN(<, RAN_NODEB_NUM_MAX, nodeBNum, O5G_RC_ER, "RAN_LoadConfig() NodeBNum[%d] error!\n", nodeBNum);

    gpRanContext->nbNum = nodeBNum;
    for (nodeLoop = 0; nodeLoop < nodeBNum; nodeLoop++)
    {
        toml_table_t* pNodeBTable;
        pNodeBTable = toml_table_at(pNodeBTableArray, nodeLoop);
        IF_RETURN(==, NULL, pNodeBTable, O5G_RC_ER, "RAN_LoadConfig() toml_table_at() error!\n");

        rc = RAN_LoadNodeBConfig(pNodeBTable, &gpRanContext->nbContext[nodeLoop]);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_LoadConfig() RAN_LoadNodeBConfig() error!\n");
    }

    toml_free(pTomlTableRoot);
    fclose(fp);

    return O5G_RC_OK;
}

int32_t RAN_ParseCmdLineOpt(int argc, char* argv[])
{
    int32_t opt;
    int32_t index;

    optind  = 0;
    struct option options[] =
    {
        { "app-type", required_argument, NULL, 't' },
        { "config", required_argument, NULL, 'c' },
        { 0, 0, 0, 0 },
    };

    while (1)
    {
        index = 0;
        opt = getopt_long(argc, argv, "", options, &index);
        IF_BREAK(==, -1, opt, NO_LOG_MESSAGE);
        switch (opt)
        {
        case 't':
            gpRanContext->appType = O5G_GetAppType(optarg);
            if (APP_TYPE_RAN != gpRanContext->appType
                && APP_TYPE_RAN_CU != gpRanContext->appType
                && APP_TYPE_RAN_DU != gpRanContext->appType)
            {
                CSP_LOG_ERR(RAN_MD_COMM, "RAN_ParseCmdLineOpt() appType[%d] error!\n", gpRanContext->appType);
                return O5G_RC_ER;
            }
            CSP_LOG_INF(RAN_MD_COMM, "appType : %s\n", gAppTypeStrTable[gpRanContext->appType]);
            break;
        case 'c':
            IF_RETURN(==, NULL, optarg, O5G_RC_ER, "RAN_ParseCmdLineOpt() config file path error!\n");
            gpRanContext->configFilePath = strdup(optarg);
            IF_RETURN(==, NULL, gpRanContext->configFilePath, O5G_RC_ER, "RAN_ParseCmdLineOpt() strdup() error!\n");
            CSP_LOG_INF(RAN_MD_COMM, "configFilePath : %s\n", gpRanContext->configFilePath);
            break;
        default:
            CSP_LOG_ERR(RAN_MD_COMM, "RAN_ParseCmdLineOpt() argv error!\n");
            return O5G_RC_ER;
        }
    }

    return O5G_RC_OK;
}

int32_t RAN_Init(int argc, char** argv)
{
    int32_t rc;
    uint32_t nbLoop;
    uint32_t cellLoop;
    S_RanNodeBContext *pNbContext;
    S_RanCellContext *pCellContext;

    rc = LOG_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Init() LOG_Init() error!\n");

    LOG_SetAppType("AX");
    LOG_SetModuleNameTable(gRanModuleNameTable, RAN_MD_BUTT, RAN_MODULE_NAME_PREFIX_LEN);

    rc = RAN_MP_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Init() RAN_MP_Init() error!\n");

    gpRanContext = OS_MemCalloc(1, sizeof(S_RanContext));
    IF_RETURN(==, NULL, gpRanContext, O5G_RC_ER, "RAN_Init() OS_MemCalloc() error!\n");

    rc = RAN_ParseCmdLineOpt(argc, argv);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Init() RAN_ParseCmdLineOpt() error!\n");

    rc = RAN_LoadConfig();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Init() RAN_LoadConfig() error!\n");

    if (APP_TYPE_RAN == gpRanContext->appType)
    {
        LOG_SetAppType("AN");
    }
    if (APP_TYPE_RAN_CU == gpRanContext->appType)
    {
        LOG_SetAppType("AC");
    }
    if (APP_TYPE_RAN_DU == gpRanContext->appType)
    {
        LOG_SetAppType("AD");
    }

    /* 当前只支持FUNC_SPLIT_OPT_0 */
    gpRanContext->funcSplitOpt = FUNC_SPLIT_OPT_0;

    CSP_LOG_INF(RAN_MD_COMM, "funcSplitOpt : %s\n", gFuncSplitOptNameTable[gpRanContext->funcSplitOpt]);

    for (nbLoop = 0; nbLoop < RAN_NODEB_NUM_MAX; nbLoop++)
    {
        pNbContext = &gpRanContext->nbContext[nbLoop];
        IF_CONTINUE(==, FALSE, pNbContext->inUse, NO_LOG_MESSAGE);
        for (cellLoop = 0; cellLoop < RAN_CELL_NUM_MAX; cellLoop++)
        {
            pCellContext = &pNbContext->cellContext[cellLoop];
            IF_CONTINUE(==, FALSE, pCellContext->inUse, NO_LOG_MESSAGE);

            pCellContext->pL2L1CpMsgQue = VarRB_Create(NULL, 2 * 1024 * 1024, VAR_RB_TYPE_MPSC);
            IF_RETURN(==, NULL, pCellContext->pL2L1CpMsgQue, O5G_RC_ER, "RAN_Init() VarRB_Create() error!\n");
            pCellContext->pL1L2CpMsgQue = VarRB_Create(NULL, 2 * 1024 * 1024, VAR_RB_TYPE_MPSC);
            IF_RETURN(==, NULL, pCellContext->pL1L2CpMsgQue, O5G_RC_ER, "RAN_Init() VarRB_Create() error!\n");

            pCellContext->pL2L1UpMsgQue = VarRB_Create(NULL, 8 * 1024 * 1024, VAR_RB_TYPE_MPSC);
            IF_RETURN(==, NULL, pCellContext->pL2L1CpMsgQue, O5G_RC_ER, "RAN_Init() VarRB_Create() error!\n");
            pCellContext->pL1L2UpMsgQue = VarRB_Create(NULL, 8 * 1024 * 1024, VAR_RB_TYPE_MPSC);
            IF_RETURN(==, NULL, pCellContext->pL1L2CpMsgQue, O5G_RC_ER, "RAN_Init() VarRB_Create() error!\n");
        }
    }

    rc = RAN_L1_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Init() RAN_L1_Init() error!\n");

    rc = RAN_L2_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Init() RAN_L2_Init() error!\n");

    rc = RAN_L3_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Init() RAN_L3_Init() error!\n");

    CSP_LOG_INF(RAN_MD_COMM, "RAN_Init() success.\n");

    return O5G_RC_OK;
}

int32_t RAN_Final(void)
{
    int32_t rc;
    uint32_t nbLoop;
    uint32_t cellLoop;
    S_RanNodeBContext *pNbContext;
    S_RanCellContext *pCellContext;

    rc = RAN_L1_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Final() RAN_L1_Final() error!\n");

    rc = RAN_L2_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Final() RAN_L2_Final() error!\n");

    rc = RAN_L3_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Final() RAN_L3_Final() error!\n");

    for (nbLoop = 0; nbLoop < RAN_NODEB_NUM_MAX; nbLoop++)
    {
        pNbContext = &gpRanContext->nbContext[nbLoop];
        IF_CONTINUE(==, FALSE, pNbContext->inUse, NO_LOG_MESSAGE);
        for (cellLoop = 0; cellLoop < RAN_CELL_NUM_MAX; cellLoop++)
        {
            pCellContext = &pNbContext->cellContext[cellLoop];
            IF_CONTINUE(==, FALSE, pCellContext->inUse, NO_LOG_MESSAGE);

            VarRB_Destroy(pCellContext->pL2L1CpMsgQue);
            VarRB_Destroy(pCellContext->pL1L2CpMsgQue);

            VarRB_Destroy(pCellContext->pL2L1UpMsgQue);
            VarRB_Destroy(pCellContext->pL1L2UpMsgQue);
        }
    }

    free(gpRanContext->configFilePath);
    OS_MemFree(gpRanContext);

    rc = RAN_MP_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Final() RAN_MP_Final() error!\n");

    CSP_LOG_INF(RAN_MD_COMM, "RAN_Final() success.\n");

    rc = LOG_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Final() LOG_Final() error!\n");

    return O5G_RC_OK;
}

int32_t RAN_Start(void)
{
    int32_t rc;

    rc = RAN_L1_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Start() RAN_L1_Start() error!\n");

    rc = RAN_L2_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Start() RAN_L2_Start() error!\n");

    rc = RAN_L3_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Start() RAN_L3_Start() error!\n");

    CSP_LOG_INF(RAN_MD_COMM, "RAN_Start() success.\n");

    return O5G_RC_OK;
}

int32_t RAN_Stop(void)
{
    int32_t rc;

    rc = RAN_L1_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Stop() RAN_L1_Stop() error!\n");

    rc = RAN_L2_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Stop() RAN_L2_Stop() error!\n");

    rc = RAN_L3_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_Stop() RAN_L3_Stop() error!\n");

    CSP_LOG_INF(RAN_MD_COMM, "RAN_Stop() success.\n");

    return O5G_RC_OK;
}
